/*
    服务注册中心客户端业务处理层：服务提供者 & 服务发现者（此处客户端不具备下层网络模块，只是业务处理模块，完整的模块需要结合网络模块一起使用）

    服务提供者：向服务注册中心注册服务
        1、提供一个注册服务的接口

    服务发现者：向服务注册中心发现服务
        1、提供一个服务发现接口
        2、提供一个服务上下线通知的回调，被注册给Dispatcher模块
        3、具有管理服务提供者的能力：一个服务可能对应多个节点提供服务，并每次rpc调用的时候应该选择哪个节点，所以需要管理起来
           3.1、采用映射表<方法，节点管理器>将其管理，此处将节点的管理从服务发现者抽离出来，然后抽象成一个类，在服务发现者类中通过节点管理器提供的方法
                来获取应该向哪个节点发起rpc请求。这样一来服务发现者变得非常容易管理，同时如果节点管理器有什么变动，几乎发现者可以不动

    节点管理器：对服务节点进行管理
        1、对外提供一个接口，能够按照某种负载均衡策略（RR）来选择一个节点
        2、提供一个增加节点的接口，可以向节点管理器添加服务节点
*/
#pragma once
#include "requestor.hpp"
#include "../common/uuid.hpp"
#include "../common/message.hpp"

namespace ns_jsonrpc
{
    namespace ns_client
    {
        // 服务提供者
        class Provider
        {
        public:
            using ProviderPtr = std::shared_ptr<Provider>;

        public:
            Provider(const Requestor::RequestorPtr &requestor) : _requestor(requestor)
            {
            }

            // 服务注册（向服务注册中心注册服务）
            bool serviceRegistry(const BaseConnection::BaseConnectionPtr &conn, const std::string &method, const Address &host)
            {
                // 1、消息组织
                ServiceRequest::ServiceRequestPtr req = MessageFactory::create<ServiceRequest>();
                req->setId(UUID::uuid());
                req->setMType(MType::REQ_SERVICE);
                req->setOpType(ServiceOpType::SERVICE_REGISTRY);
                req->setMethod(method);
                req->setHost(host);

                // 2、发送消息 & 接收响应
                BaseMessage::BaseMessagePtr rsp;
                _requestor->send(conn, req, rsp); // 同步发送

                // 3、处理响应
                ServiceResponse::ServiceResponsePtr srvRsp = std::dynamic_pointer_cast<ServiceResponse>(rsp);
                if (srvRsp == nullptr || srvRsp->check() == false)
                {
                    LOG_ERROR("method registry failed: invalid response\n");
                    return false;
                }
                if (srvRsp->rcode() != RCode::RCODE_OK)
                {
                    LOG_ERROR("method registry failed: %s\n", errReason(srvRsp->rcode()).c_str());
                    return false;
                }
                LOG_INFO("method registry success, method name: %s\n", method.c_str());
                return true;
            }

        private:
            Requestor::RequestorPtr _requestor; // 消息的发送需要依赖Requestor模块的管理
        };

        // 节点管理器
        class NodeManager
        {
        public:
            using NodeManagerPtr = std::shared_ptr<NodeManager>;

        public:
            NodeManager() : _index(0)
            {
            }
            NodeManager(const std::vector<Address> &nodes) : _nodes(nodes), _index(0)
            {
            }

            // 选择一个节点
            Address chooseNode()
            {
                // 采用RR负载均衡策略
                std::unique_lock<std::mutex> lock(_mutex);
                if (_nodes.size() > 0)
                    return _nodes[_index++ % _nodes.size()];
                else
                    return Address();
            }

            // 添加一个节点
            void appendNode(const Address &node)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _nodes.push_back(node);
            }

            // 删除一个节点
            void removeNode(const Address &node)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                for (auto it = _nodes.begin(); it != _nodes.end(); ++it)
                {
                    if (*it == node)
                    {
                        _nodes.erase(it);
                        break;
                    }
                }
            }

            // 是否存在服务节点
            bool isEmpty()
            {
                std::unique_lock<std::mutex> lock(_mutex);
                return _nodes.empty();
            }

        private:
            std::vector<Address> _nodes;
            size_t _index;
            std::mutex _mutex;
        };

        // 服务发现者
        class Discoverer
        {
        public:
            using DiscovererPtr = std::shared_ptr<Discoverer>;
            using OfflineCallback = std::function<void(const Address &host)>; // 服务下线回调，由上层/客户端提供做更多的事情（当服务下线的时候，用于删除rpc客户端的长连接池对象）

        public:
            Discoverer(const Requestor::RequestorPtr &requestor, const OfflineCallback &offlineCallback)
                : _requestor(requestor), _offline_callback(offlineCallback)
            {
            }

            // 服务发现（先在本地维护的_method_nodes容器中查找，没有则取服务注册中心发现）
            bool serviceDiscovery(const BaseConnection::BaseConnectionPtr &conn, const std::string &method, Address &host)
            {
                // 1、在本地维护的_method_nodes容器中查找
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it = _method_nodes.find(method);
                    if (it != _method_nodes.end() && !it->second->isEmpty())
                    {
                        host = it->second->chooseNode();
                        LOG_INFO("method discovery success: %s:%d -> %s\n", host.first.c_str(), host.second, method.c_str());
                        return true;
                    }
                }
                // 2、本地没有找到服务的缓存，或者服务的缓存为空（提供服务的节点全部下线），则取服务注册中心进行服务发现
                // 2.1、消息组织
                ServiceRequest::ServiceRequestPtr req = std::make_shared<ServiceRequest>();
                req->setId(UUID::uuid());
                req->setMType(MType::REQ_SERVICE);
                req->setOpType(ServiceOpType::SERVICE_DISCOVERY);
                req->setMethod(method);

                // 2.2、发送消息 & 接收响应
                BaseMessage::BaseMessagePtr rsp;
                _requestor->send(conn, req, rsp);

                // 2.3、处理响应
                ServiceResponse::ServiceResponsePtr srvRsp = std::dynamic_pointer_cast<ServiceResponse>(rsp);
                if (srvRsp == nullptr || srvRsp->check() == false)
                {
                    LOG_ERROR("method discovery failed: invalid response\n");
                    return false;
                }
                if (srvRsp->rcode() != RCode::RCODE_OK)
                {
                    LOG_ERROR("method discovery failed: %s\n", errReason(srvRsp->rcode()).c_str());
                    return false;
                }

                // 2.4、处理成功，将节点缓存起来
                std::unique_lock<std::mutex> lock(_mutex);
                NodeManager::NodeManagerPtr nodeManager = std::make_shared<NodeManager>(srvRsp->hosts());
                _method_nodes[method] = nodeManager;
                host = nodeManager->chooseNode();
                LOG_INFO("method discovery success: %s:%d -> %s\n", host.first.c_str(), host.second, method.c_str());
                return true;
            }

            // 服务上下线通知回调，被注册给Dispatcher模块
            void onServiceRequest(const BaseConnection::BaseConnectionPtr &conn, const ServiceRequest::ServiceRequestPtr &msg)
            {
                // body检查
                if (!msg->check())
                {
                    LOG_ERROR("service online notify failed, invalid message\n");
                    return;
                }
                ServiceOpType optype = msg->opType();
                std::unique_lock<std::mutex> lock(_mutex);
                if (optype == ServiceOpType::SERVICE_ONLINE)
                {
                    // 上线通知：缓存服务节点（此处分为两种情况：第一：有缓存，第二：没有缓存）
                    NodeManager::NodeManagerPtr nodeManager = _method_nodes[msg->method()];
                    nodeManager->appendNode(msg->host());
                    LOG_INFO("online notify: %s:%d -> %s\n", msg->host().first.c_str(), msg->host().second, msg->method().c_str());
                }
                else if (optype == ServiceOpType::SERVICE_OFFLINE)
                {
                    // 下线通知：删除服务节点
                    LOG_INFO("offline notify: %s:%d -> %s\n", msg->host().first.c_str(), msg->host().second, msg->method().c_str());
                    auto it = _method_nodes.find(msg->method());
                    if (it == _method_nodes.end())
                        return;
                    it->second->removeNode(msg->host());

                    // 回调
                    _offline_callback(msg->host());
                }
                else
                {
                    // 未知服务操作类型
                    LOG_ERROR("service registry has received an unknown service operation type\n");
                }
            }

        private:
            Requestor::RequestorPtr _requestor;
            std::unordered_map<std::string, NodeManager::NodeManagerPtr> _method_nodes;
            std::mutex _mutex;
            OfflineCallback _offline_callback;
        };
    }
}
